using System.ComponentModel;
using System.Runtime.Serialization;

using SharpGL.Media3D.NETDesignSurface.Converters;

namespace SharpGL.Media3D
{
    /// <summary>
    /// This class provides a means of working with standard and OpenGL colours.
    /// Use the ColorNet and ColorGL properties to set or access the colour in either
    /// mode.
    /// </summary>
    [TypeConverter(typeof(GLColorConverter))]
    [DataContract(/*IsReference = true, */Name = "GLColor", Namespace = "SharpGL")]
	public struct GLColor
    {
        #region Private Fields

        [DataMember()]
        private float r;

        [DataMember()]
        private float g;

        [DataMember()]
        private float b;

        [DataMember()]
        private float a;

        #endregion Protected Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="GLColor"/> class.
        /// </summary>
        public GLColor(float r, float g, float b, float a)
		{
			this.r = r;
            this.g = g;
            this.b = b;
            this.a = a;
		}

        /// <summary>
        /// Initializes a new instance of the <see cref="GLColor"/> class.
        /// </summary>
        public GLColor(GLColor color)
        {
            this.r = color.r;
            this.g = color.g;
            this.b = color.b;
            this.a = color.a;
        }

        #endregion Constructors

        #region Public Methods

        public override int GetHashCode()
        {
            return this.r.GetHashCode() ^ this.g.GetHashCode() ^ this.b.GetHashCode() ^ this.a.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj == null || !(obj is GLColor))
            {
                return false;
            }

            return Equals(this, (GLColor)obj);
        }

        public bool Equals(GLColor color)
        {
            return Equals(this, color);
        }

        public static bool Equals(GLColor lhs, GLColor rhs)
        {
            return lhs.r.Equals(rhs.r) && lhs.g.Equals(rhs.g) && lhs.b.Equals(rhs.b) && lhs.a.Equals(rhs.a);
        }

        public override string ToString()
        {
            return "(" + this.R + ", " + this.G + ", " + this.B + ", " + this.A + ")";
        }

        public void Clamp()
        {
            if (r > 1.0f) r = 1.0f;
            else if (r < 0.0f) r = 0.0f;

            if (g > 1.0f) g = 1.0f;
            else if (g < 0.0f) g = 0.0f;

            if (b > 1.0f) b = 1.0f;
            else if (b < 0.0f) b = 0.0f;

            if (a > 1.0f) a = 1.0f;
            else if (a < 0.0f) a = 0.0f;
        }

        public void Set(float r, float g, float b)
        {
            this.r = r;
            this.g = g;
            this.b = b;
        }

        #endregion Public Methods

        #region Public Operators

        public static implicit operator System.Drawing.Color(GLColor color)
        {
            return System.Drawing.Color.FromArgb((int)(color.a * 255), (int)(color.r * 255.0f),
                (int)(color.g * 255.0f), (int)(color.b * 255.0f));
        }

        public static implicit operator GLColor(System.Drawing.Color color)
        {
            GLColor col = new GLColor();
            col.ColorNET = color;
            return col;
        }

        public static implicit operator float[](GLColor color)
        {
            return new float[] { color.r, color.g, color.b, color.a };
        }

        public static GLColor operator *(GLColor lhs, GLColor rhs)
        {
            return new GLColor(lhs.r * rhs.r, lhs.g * rhs.g, lhs.b * rhs.b, lhs.a * rhs.a);
        }

        public static GLColor operator *(GLColor lhs, float rhs)
        {
            return new GLColor(lhs.r * rhs, lhs.g * rhs, lhs.b * rhs, lhs.a * rhs);
        }

        public static GLColor operator +(GLColor lhs, GLColor rhs)
        {
            return new GLColor(lhs.r + rhs.r, lhs.g + rhs.g, lhs.b + rhs.b, lhs.a + rhs.a);
        }

        public static bool operator ==(GLColor color1, GLColor color2)
        {
            return color1.Equals(color2);
        }

        public static bool operator !=(GLColor color1, GLColor color2)
        {
            return !color1.Equals(color2);
        }

        #endregion Public Operators

        #region Public Properties

        /// <summary>
		/// This property allows you to access the color as if it was a .NET
		/// color.
		/// </summary>
        public System.Drawing.Color ColorNET
        {
            get
            {
                System.Drawing.Color col = System.Drawing.Color.FromArgb((int)(r * 255.0f),
                    (int)(g * 255.0f), (int)(b * 255.0f));
                return col;
            }
            set
            {
                System.Drawing.Color col = value;
                r = (float)col.R / 255.0f;
                g = (float)col.G / 255.0f;
                b = (float)col.B / 255.0f;
                a = (float)col.A / 255.0f;
            }
        }

		/// <summary>
		/// This property accesses the color as an OpenGL value.
		/// </summary>
        public float[] ColorGL
        {
            get { return new float[] { r, g, b, a }; }
            set
            {
                r = value[0];
                g = value[1];
                b = value[2];
                a = value[3];
            }
        }

        /// <summary>
        /// Gets or sets the red component.
        /// </summary>
        /// <value>
        /// The R.
        /// </value>
        public float R
        {
            get { return r; }
            set { r = value; }
        }

        /// <summary>
        /// Gets or sets the green component.
        /// </summary>
        /// <value>
        /// The G.
        /// </value>
        public float G
        {
            get { return g; }
            set { g = value; }
        }

        /// <summary>
        /// Gets or sets the blue component.
        /// </summary>
        /// <value>
        /// The b.
        /// </value>
        public float B
        {
            get { return b; }
            set { b = value; }
        }

        /// <summary>
        /// Gets or sets the alpha component.
        /// </summary>
        /// <value>
        /// The a.
        /// </value>
        public float A
        {
            get { return a; }
            set { a = value; }
        }

        #endregion Public Properties
    }
}